"""
pirates

The various types of people that inhabit ships, ports,
forts, and islands (not just pirates).
"""

import logging, random

import pgEngine.dice as dice, pgEngine.boat as boat, pgEngine.skills as skills
import pgEngine.weapons as weapons, pgEngine.baseClasses as baseClasses
import pgEngine.movementData as movementData

#TODO: Write classes for the various special figs

BLINDEYES = (BE_NONE, 
             BE_LEFTPATCH, 
             BE_LEFTGLASS, 
             BE_RIGHTPATCH, 
             BE_RIGHTGLASS) = range(5)

PEGLEGS = (PL_NONE, 
           PL_LEFT, 
           PL_RIGHT, 
           PL_BOTH) = range(4)

HOOKHANDS = (HH_NONE, 
             HH_LEFT, 
             HH_RIGHT) = range(3)

WEAPONS = (WE_NONE, 
           WE_PISTOL, 
           WE_RIFLE, 
           WE_MUSKET, 
           WE_CUTLASS, 
           WE_KNIFE, 
           WE_GRENADE, 
           WE_AXE, 
           WE_OAR, 
           WE_SHOVEL) = range(10)

PERSON_ROLES = (RO_ADMIRAL, 
                RO_CAPTAIN, 
                RO_QUARTERMASTER, 
                RO_FIRSTMATE, 
                RO_BOATSWAIN, 
                RO_CABINBOY, 
                RO_CARPENTER, 
                RO_GUNNER, 
                RO_PILOT, 
                RO_NAVIGATOR, 
                RO_STRIKER, 
                RO_SURGEON, 
                RO_COOK, 
                RO_SECONDMATE, 
                RO_HAND, 
                RO_TRADER, 
                RO_NATIVE, 
                RO_STATESMAN, 
                RO_HOMUNCULUS) = range(19)
 
class Person(baseClasses.Attackable, baseClasses.Attacker):
    # Weapons, tools, etc.
    leftItem = WE_NONE
    rightItem = WE_NONE
    
    # Various prosthetics
    pegLeg = PL_NONE
    hookHand = HH_NONE
    blindEye = BE_NONE
    """
    If hook hands can't hold anything, why not make 
    it an item, rather than its own property?
    """
    
    # Decoration
    cape = None
    hat = None
    
    # Standard duties
    role = RO_HAND
    duties = None
    _dutyMem = None     # saved state of duties
    # Skills
    skills = None
    
    # Personal Inventory
    equipment = None

    # Health, injuries
    maxHealth = 10.0
    _health = maxHealth
    injuries = None
    tohit = 10
    
    #The thing that knows where I am and how far it is to other things.
    parent = None
    
    logger = logging.getLogger("engine.person")
    
        
    def __init__(self, leftItem=None, rightItem=None, role=None):
        self.injuries = []

        self.role = role
        self.skills = {}
        self._assignDuties()

        self.leftItem = leftItem
        self.rightItem = rightItem
        self.equipment = []
        if leftItem:
            self.equipment.append(leftItem)
        
        if rightItem:
            self.equipment.append(rightItem)
        
    def _assignDuties(self):
        self.duties = {}
        self._dutyMem = {}
        for mode in boat.BOAT_MODES:
            self.duties[mode] = skills.Duty(self.role, mode)
        
    
    def move(self, direction, distance):
        if hasattr(self, 'parent'):
            self.parent.moveItem(self, movementData.Move(direction, distance))
        else:
            #Figure out what to do in the old interface later...
            pass
    
    def hasWeapon(self, weapon):
        return weapon is self.leftItem or weapon is self.rightItem
    
    def attack(self, target, weapon):
        self.logger.debug('Attempting to attack %s with %s' % (target, weapon))
        if not self.hasWeapon(weapon):
                self.logger.debug("  I'm not holding that weapon!")
                return False
        if not self.inFirePhase:
            self.logger.debug("  Not time to attack!")
            return False
        if self.hasFired:
            self.logger.debug("  I've already attacked this turn.")
            return False
        if self.parent is target.parent:
            #Same boat, island, etc.
            distToTarget = self.parent.getDistance(self, target)
        elif self.parent.parent is target.parent.parent:
            #If person on boat firing to person on island, then try...
            distToTarget = self.parent.parent.getDistance(self.parent,
                                                          target.parent)
        else:
            self.logger.warning("Could not get a distance to the target. Failing to attack.")
            return False
        #TODO: Query the parent to determine if we're blocked, and by what.
        #For now, assume we are obscured, only works on boats.
        weapon.fire(target, True, distToTarget)
        return True
    
    def parry(self, weapon, damage):
        return self.leftItem.parry(weapon) or self.rightItem.parry(weapon)
    
    def takeDamage(self, amount, dtype=None):
        self.logger.debug("Taking %s of %s damage" % (amount, dtype))
        self._health -= amount
        if self._health <= 0:
            self.logger.debug("%s has died." % self)
    
    def action(self, mode):
        """
        Perform scheduled duties or special assignments,
        based on <mode>.
        """
        # Perform our assigned role's duties.
        self.duties[mode].perform(self)

    def _setHealth(self, newHealth):
        self._health = min(self.maxHealth, newHealth)

    health = property((lambda self: self._health), _setHealth)

class StowAway(Person):
    pass


class Slave(Person):
    pass


class Homunculus(Person):
    # Pet parrot, monkey, cat, dog, etc.
    # Almost definitely not a person,
    #   but mimics one.
    pass


class Pirate(Person):
    # Also includes pirate captains,
    # pirate admiralty, etc.
    boss = None
    homunculus = None
    
    # Special assignments, keyed by BOAT_MODES
    assignments = None

    logger = logging.getLogger("engine.pirate")
    id = -1
    
    def __init__(self, captain=None, 
                       id=-1,
                       leftItem=WE_PISTOL, 
                       rightItem=WE_CUTLASS, 
                       role=RO_HAND):
        Person.__init__(self, leftItem, rightItem, role)
        self.boss = captain
        self.assignments = {}
        self.id = id
    
    def __str__(self):
        return "%s #%s" % (type(self).__name__, self.id)
    
    def assign(self, mode, activity, period):
        self.assignments[mode] = (activity, period)
    
    def action(self, mode):
        if mode in self.assignments:
            # Perform the assigned task until period runs out.
            activity, period = self.assignments[mode]
            
            activity.perform(self)
            
            period -= 1
            if period > 0:
                self.assignments[mode] = (activity, period)
        
        else:
            Person.action(self, mode)

    def _assignDuties(self):
        self.duties = {}
        self._dutyMem = {}
        for mode in boat.BOAT_MODES:
            self.duties[mode] = skills.PirateDuty(self.role, mode)


def randomPirate(captain=None, id=-1):
    # Pirates who have standard skills
    logger = logging.getLogger("engine.randomPirate")
    
    logger.debug("Creating random pirate.")
    
    left = WE_PISTOL
    right = WE_CUTLASS
    
    # Hook hand
    hh = dice.dice1d12.roll()
    if hh == 1:
        hh = HH_RIGHT
        right = WE_NONE
        left = WE_PISTOL
        logger.debug("  Right hook hand.")
    elif hh == 2:
        hh = HH_LEFT
        left = WE_NONE
        right = WE_PISTOL
        logger.debug("  Left hook hand.")
    else:
        hh = HH_NONE
    
    logger.debug("  Left item: %i" % left)
    logger.debug("  Right item: %i" % right)
    
    # Peg leg
    pl = dice.dice1d12.roll()
    if pl < 4:
        pl = 4 - pl
        if pl == 1:
            logger.debug("  Both peg legs.")
        elif pl == 2:
            logger.debug("  Right peg leg.")
        elif pl == 3:
            logger.debug("  Left peg leg.")
        else:
            logger.debug("  No peg leg.")
    else:
        pl = PL_NONE
    
    # Blind eye
    be = dice.dice1d12.roll()
    if be == 1:
        be = dice.dice1d4.roll()
        if be == 1:
            logger.debug("  Left eye patch.")
        elif be == 2:
            logger.debug("  Left glass eye.")
        elif be == 3:
            logger.debug("  Right eye patch.")
        else:
            logger.debug("  Right glass eye.")

    else:
        be = BE_NONE
    
    pirate = Pirate(captain, id, left, right)
    pirate.blindEye = be
    pirate.pegLeg = pl
    pirate.hookHand = hh
    
    logger.debug( "Finished creating random pirate, %s." % str(pirate) )
    return pirate


def randomCaptain(id):
    logger = logging.getLogger("engine.randomCaptain")

    logger.debug("Creating pirate captain.")

    captain = randomPirate(None, id)
    captain.hookHand = HH_NONE
    if captain.blindEye is not BE_NONE:
        captain.blindEye = BE_LEFTPATCH
    if captain.pegLeg is PL_BOTH:
        captain.pegLeg = PL_RIGHT
    captain.skills[skills.ST_NAVIGATE] = 3
    captain.skills[skills.ST_LUCK] = dice.dice1d4.roll()
    logger.debug("  Luck: +%i" % captain.skills[skills.ST_LUCK])
    captain.skills[(skills.ST_WEAPON, WE_CUTLASS)] = 3
    nSkill = 12
    while nSkill == 12:
        nSkill, qtySkill = addRandomSkill(captain, dice.dice1d10)
    logger.debug("  Special skill: #%s, +%i" % (nSkill, qtySkill))
    
    logger.debug( "Finished creating pirate captain, %s." % str(captain) )
    return captain

def addRandomSkill(pirate, qtyroll = dice.dice1d6):
    nSkill = random.choice(skills.SKILL_TYPES)
    while nSkill in pirate.skills:
        nSkill = random.choice(skills.SKILL_TYPES)

    # Record specific weapon alongside ST_WEAPON in pirate.skills
    if nSkill == skills.ST_WEAPON:
        weaponSkill = 0
        while not weaponSkill:
            weaponSkill = random.choice(WEAPONS)
        nSkill = (nSkill, weaponSkill)  
        
    qtySkill = (qtyroll.rollSum() + 1) // 2
    pirate.skills[nSkill] = qtySkill
    return nSkill, qtySkill
    

def randomSkilledPirate(captain=None, id=-1, skillroll=dice.dice1d4):
    """ Create a pirate with special skill(s). """

    logger = logging.getLogger("engine.randomSkilledPirate")
    
    logger.debug("Creating skilled pirate.")

    # Start with a standard random pirate
    pirate = randomPirate(captain, id)
    
    # Add skillroll skills
    for n in range(skillroll.rollSum()):
        nSkill, qtySkill = addRandomSkill(pirate)        
        logger.debug("  Special skill: #%s, +%i" % (nSkill, qtySkill))
        
    logger.debug( "Finished creating skilled pirate, %s." % str(pirate) )
    
    return pirate



class MilitaryMan(Person):
    # Police officer, Royal Navy, Privateer, etc.
    # Enforcer of laws.
    pass


class CorporateMan(Person):
    # Crew/Captain for East India, Royal African, etc.
    # State-Sanctioned Trader.
    pass


class Statesman(Person):
    # eg. Major-General in Penzance
    #     Governor Swann in PotC
    pass


class MainlandTrader(Person):
    # Staffs a State-Sanctioned Trading Port
    pass


class PirateTrader(Person):
    # Staffs a Pirate (Free) Trading Port
    pass


class IslandNative(Person):
    pass


if __name__ == "__main__":
    for n in range(40):
        print randomSkilledPirate().skills
